home *** CD-ROM | disk | FTP | other *** search
- /* SampleDeviceOutput.c */
- /*****************************************************************************/
- /* */
- /* Out Of Phase: Digital Music Synthesis on General Purpose Computers */
- /* Copyright (C) 1994 Thomas R. Lawrence */
- /* */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to the Free Software */
- /* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- /* */
- /* Thomas R. Lawrence can be reached at tomlaw@world.std.com. */
- /* */
- /*****************************************************************************/
-
- #include "MiscInfo.h"
- #include "Audit.h"
- #include "Debug.h"
- #include "Definitions.h"
-
- #include "SampleDeviceOutput.h"
- #include "SampleConsts.h"
- #include "ExecuteSynthesis.h"
- #include "Memory.h"
- #include "SoundOutput.h"
- #include "Alert.h"
- #include "EventLoop.h"
- #include "ClipWarnDialog.h"
- #include "SynthProgressWindow.h"
- #include "ErrorDaemon.h"
-
-
- #define NUMBEROFBUFFERS (8)
- #define INITIALNUMBUFFERS (2)
-
- #define CANCELCHECKTIMER (50)
-
-
- typedef struct
- {
- MyBoolean UseStereo;
- long SamplingRate;
- OutputNumBitsType NumBitsOut;
-
- void* CurrentBuffer;
- long TotalFramesPerBuffer;
- long CurrentBufferIndex;
- long TotalSampleCount;
- long ClippedSampleCount;
- largefixedsigned MaxClipExtent;
-
- long CancelCheck;
-
- SynthWinRec* Window;
- } StateRecord;
-
-
- static MyBoolean CallbackRoutine(StateRecord* Info, largefixedsigned* DataBlock,
- long NumFrames, MyBoolean* AbortPlaybackFlagOut)
- {
- long Limit;
- long BufferOffset;
- void* Buffer;
- long Scan;
- largefixedsigned TempValue;
-
- Info->CancelCheck -= 1;
- if (Info->CancelCheck < 0)
- {
- Info->CancelCheck = CANCELCHECKTIMER;
- if (RelinquishCPUJudiciouslyCheckCancel())
- {
- *AbortPlaybackFlagOut = True;
- return True;
- }
- }
-
- if (NumFrames + Info->CurrentBufferIndex > Info->TotalFramesPerBuffer)
- {
- /* submit the buffer & get another */
- SubmitBuffer((char*)Info->CurrentBuffer,Info->CurrentBufferIndex,NIL,NIL);
- Info->CurrentBuffer = NIL;
- do
- {
- Info->CurrentBuffer = CheckOutSoundBuffer();
- if (Info->CurrentBuffer == NIL)
- {
- /* spinwait until a buffer is available */
- if (RelinquishCPUCheckCancel())
- {
- *AbortPlaybackFlagOut = True;
- return True;
- }
- }
- } while (Info->CurrentBuffer == NIL);
- Info->CurrentBufferIndex = 0;
-
- if (NumFrames + Info->CurrentBufferIndex > Info->TotalFramesPerBuffer)
- {
- /* buffer overrun! */
- AlertHalt("The output buffer space is too small. Increase the "
- "number of bytes allocated to sound buffering.",NIL);
- return False;
- }
- }
-
- Limit = NumFrames;
- BufferOffset = Info->CurrentBufferIndex;
- if (Info->UseStereo)
- {
- Limit *= 2; /* twice as many frames for stereo */
- BufferOffset *= 2;
- }
- if (Info->NumBitsOut == eOutput8Bits)
- {
- /* conversion to 8-bits */
- Buffer = (signed char*)(Info->CurrentBuffer) + BufferOffset;
- for (Scan = 0; Scan < Limit; Scan += 1)
- {
- PRNGCHK(DataBlock,&(DataBlock[Scan]),sizeof(DataBlock[Scan]));
- TempValue = DataBlock[Scan] + double2largefixed((double)1 / 65536);
- if (TempValue > int2largefixed(1) - 1)
- {
- Info->ClippedSampleCount += 1;
- if (TempValue > Info->MaxClipExtent)
- {
- Info->MaxClipExtent = TempValue;
- }
- TempValue = int2largefixed(1) - 1;
- }
- else if (TempValue < - (int2largefixed(1) - 1))
- {
- Info->ClippedSampleCount += 1;
- if (- TempValue > Info->MaxClipExtent)
- {
- Info->MaxClipExtent = - TempValue;
- }
- TempValue = - (int2largefixed(1) - 1);
- }
- PRNGCHK(Info->CurrentBuffer,&(((signed char*)Buffer)[Scan]),
- sizeof(((signed char*)Buffer)[Scan]));
- ((signed char*)Buffer)[Scan] = TempValue >> (largefixed_precision - 8 + 1);
- }
- }
- else
- {
- /* conversion to 16-bits */
- Buffer = (signed short*)(Info->CurrentBuffer) + BufferOffset;
- for (Scan = 0; Scan < Limit; Scan += 1)
- {
- PRNGCHK(DataBlock,&(DataBlock[Scan]),sizeof(DataBlock[Scan]));
- TempValue = DataBlock[Scan] + double2largefixed((double)1 / 65536);
- if (TempValue > int2largefixed(1) - 1)
- {
- Info->ClippedSampleCount += 1;
- if (TempValue > Info->MaxClipExtent)
- {
- Info->MaxClipExtent = TempValue;
- }
- TempValue = int2largefixed(1) - 1;
- }
- else if (TempValue < - (int2largefixed(1) - 1))
- {
- Info->ClippedSampleCount += 1;
- if (- TempValue > Info->MaxClipExtent)
- {
- Info->MaxClipExtent = - TempValue;
- }
- TempValue = - (int2largefixed(1) - 1);
- }
- PRNGCHK(Info->CurrentBuffer,&(((signed short*)Buffer)[Scan]),
- sizeof(((signed short*)Buffer)[Scan]));
- ((signed short*)Buffer)[Scan] = TempValue >> (largefixed_precision - 16 + 1);
- }
- }
- Info->CurrentBufferIndex += NumFrames;
-
- Info->TotalSampleCount += NumFrames;
-
- UpdateSynthWindow(Info->Window,Info->SamplingRate,Info->TotalSampleCount,
- Info->ClippedSampleCount,False);
-
- return True;
- }
-
-
- /* this routine opens the sound channel & performs resampling to it. */
- void SynthToSoundDevice(struct MainWindowRec* MainWindow,
- struct ArrayRec* ListOfTracks, struct TrackObjectRec* KeyTrack,
- long FrameToStartAt, long SamplingRate, long EnvelopeRate,
- MyBoolean UseStereo, LargeBCDType DefaultBeatsPerMinute,
- LargeBCDType OverallVolumeScalingReciprocal,
- MyBoolean InterpOverTime, MyBoolean InterpAcrossWaves,
- LargeBCDType ScanningGap, OutputNumBitsType NumBitsOut,
- LargeBCDType SecondsOfBuffering, MyBoolean ClipWarn)
- {
- StateRecord StateInfo;
- SynthErrorCodes SynthErrorReturnCode;
- ErrorDaemonRec* ErrorDaemon;
-
- CheckPtrExistence(MainWindow);
- CheckPtrExistence(ListOfTracks);
- CheckPtrExistence(KeyTrack);
- if (SecondsOfBuffering < Double2LargeBCD(1))
- {
- SecondsOfBuffering = Double2LargeBCD(1);
- }
- if (SamplingRate < MINSAMPLINGRATE)
- {
- SamplingRate = MINSAMPLINGRATE;
- }
-
- StateInfo.UseStereo = UseStereo;
- StateInfo.SamplingRate = SamplingRate;
- StateInfo.NumBitsOut = NumBitsOut;
-
- StateInfo.CurrentBuffer = NIL;
- StateInfo.TotalFramesPerBuffer = (LargeBCD2Single(SecondsOfBuffering)
- * SamplingRate) / NUMBEROFBUFFERS;
- StateInfo.CurrentBufferIndex = 0;
- StateInfo.TotalSampleCount = 0;
- StateInfo.ClippedSampleCount = 0;
- StateInfo.MaxClipExtent = 0;
-
- StateInfo.CancelCheck = 0;
-
- StateInfo.Window = NewSynthWindow(EnvelopeRate / 2,True/*show clipping*/);
- if (StateInfo.Window == NIL)
- {
- AlertHalt("There is not enough memory available to perform synthesis.",NIL);
- SetupFailurePoint1:
- return;
- }
-
- if (!OpenSoundChannel(SamplingRate,UseStereo ? eStereo : eMono,
- (NumBitsOut == eOutput8Bits) ? e8bit : e16bit,StateInfo.TotalFramesPerBuffer,
- NUMBEROFBUFFERS,INITIALNUMBUFFERS))
- {
- AlertHalt("Unable to open the sound output device.",NIL);
- SetupFailurePoint2:
- DisposeSynthWindow(StateInfo.Window);
- goto SetupFailurePoint1;
- }
-
- StateInfo.CurrentBuffer = CheckOutSoundBuffer();
- if (StateInfo.CurrentBuffer == NIL)
- {
- /* there should initially be a buffer available */
- AlertHalt("Unable to access sound buffer.",NIL);
- SetupFailurePoint3:
- CloseSoundChannel(NIL,NIL);
- goto SetupFailurePoint2;
- }
-
- ErrorDaemon = NewErrorDaemon();
- if (ErrorDaemon == NIL)
- {
- AlertHalt("There is not enough memory available to perform synthesis.",NIL);
- SetupFailurePoint4:
- goto SetupFailurePoint3;
- }
-
- SynthErrorReturnCode = Synthesizer(MainWindow,
- (MyBoolean (*)(void*,largefixedsigned*,long,MyBoolean*))&CallbackRoutine,
- &StateInfo,ListOfTracks,KeyTrack,FrameToStartAt,SamplingRate,EnvelopeRate,
- UseStereo,DefaultBeatsPerMinute,OverallVolumeScalingReciprocal,InterpOverTime,
- InterpAcrossWaves,ScanningGap,ErrorDaemon);
-
- UpdateSynthWindow(StateInfo.Window,StateInfo.SamplingRate,StateInfo.TotalSampleCount,
- StateInfo.ClippedSampleCount,True);
-
- if ((StateInfo.CurrentBuffer != NIL) && (SynthErrorReturnCode == eSynthDone))
- {
- SubmitBuffer((char*)StateInfo.CurrentBuffer,StateInfo.CurrentBufferIndex,NIL,NIL);
- StateInfo.CurrentBuffer = NIL;
- }
-
- switch (SynthErrorReturnCode)
- {
- default:
- EXECUTE(PRERR(AllowResume,
- "SynthToSoundDevice: bad return code from Synthesizer()"));
- break;
- case eSynthDone:
- break;
- case eSynthNoMemory:
- AlertHalt("There is not enough memory available to continue synthesis.",NIL);
- break;
- case eSynthUserCancelled:
- break;
- /* case eSynthProgramError: */
- break;
- case eSynthPrereqError:
- break;
- case eSynthUndefinedInstrumentError:
- break;
- case eSynthDataSubmitError:
- AlertHalt("An error occurred while sending data to output device.",NIL);
- break;
- case eSynthDuplicateNames:
- break;
- }
-
- if (SynthErrorReturnCode == eSynthUserCancelled)
- {
- KillSoundChannel(); /* don't wait for buffers to play out */
- }
- else
- {
- CloseSoundChannel(NIL,NIL);
- }
- DisposeSynthWindow(StateInfo.Window);
-
- if (ErrorDaemonDidClampingOccur(ErrorDaemon))
- {
- ClampWarnDialog(ErrorDaemonGetMaxClamping(ErrorDaemon),
- ErrorDaemonGetMaxClamping(ErrorDaemon)
- * LargeBCD2Double(OverallVolumeScalingReciprocal));
- }
- else
- {
- if (ClipWarn && (StateInfo.ClippedSampleCount != 0))
- {
- ClipWarnDialog(StateInfo.ClippedSampleCount,StateInfo.TotalSampleCount,
- largefixed2double(StateInfo.MaxClipExtent),
- largefixed2double(StateInfo.MaxClipExtent)
- * LargeBCD2Double(OverallVolumeScalingReciprocal),False);
- }
- }
-
- DisposeErrorDaemon(ErrorDaemon);
- }
-